#ifndef RENDERER_HPP
#define RENDERER_HPP

//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include <string>
#include <Math/Matrix.hpp>
#include <Math/Vector.hpp>
#include <Rendering/Camera.hpp>
#include <Rendering/Shader.hpp>
#include <Rendering/Enumeration.hpp>
#include <Rendering/Projection.hpp>
#include <Rendering/Texture.hpp>
#include <Rendering/Projection.hpp>


namespace Rendering {

class RessourcesProxy;
//-----------------------------------------------------------------------------
class FPSCounter {
public:	
	// Update frame rate
	static void Update() { 
		// This function calculates FPS static 
		static int fps = 0; 
		static float previousTime = 0.0f; 
#ifdef WIN32		
		float currentTime = (GetTickCount() * 0.001f);
#else
		float currentTime = 0.0f; // TODO implement in UNIX env
#endif				
		++fps; // Increment the FPS counter 
		// Display FPS 
		float elapsedTime = currentTime - previousTime;		
		if( elapsedTime > 1.0f )	{
			previousTime = currentTime; 
			framerate = fps;			
			fps = 0;
		} 
	} 
	// Return current frame rate
	static float GetFrameRate() { return framerate; }
private:	
	// Current frame rate
	static float framerate;
	// Constructor	
	FPSCounter();
	~FPSCounter();
	FPSCounter(const FPSCounter& copy);	
	FPSCounter& operator=(const FPSCounter& copy);
};
//-----------------------------------------------------------------------------
class RenderingSurface {
public:
	// Set window size
	void SetSize(int width, int height);
	// Set mouse position
	void WarpMousePosition(int x, int y);
	// Destroy rendering surface
	void Finalize();
	// Initialize rendering surface
	void Initialize(int argc, char** argv, const char * title,
			void (*TDisplay)(void), void (*TReshape)(int, int),
			void (*TKeyboard)(unsigned char , int, int),
			void (*TSpecial)(int , int, int),
			void (*TMouse)(int, int, int, int),
			void (*TMotion)(int, int) ) const;	
	// Viewport size
	int surfaceWidth;
	int surfaceHeight;
	// Return the renderer instance
	static RenderingSurface& Instance();
private:	
	// Constructor	
	RenderingSurface();
	RenderingSurface(const RenderingSurface& copy);	
	RenderingSurface& operator=(const RenderingSurface& copy);
};
//-----------------------------------------------------------------------------
class Renderer {
public:	
	// Type of projection
	enum ProjectionType {PERSPECTIVE, ORTHOGONAL};
	// Initialize renderer
	void Initialize() const;
	// Initialize rendering
	void StartRender() const;
	// Launch rendering thread
	void Run() const;
	// Finalize rendering
	void EndRender() const;
	// Set point of view	
	void SetPointOfView(const Matrix::Matrix4& transformation) const;
	void SetPointOfView(IPointOfView* pov) const;
	// Set projection
	void SetProjection(ProjectionType type, const IPointOfView& camera) const;
	// Draw overlay
	void Draw(int x, int y, std::string value) const;
	// Draw a mesh
	void Draw() const;	
	// Load object id on OpenGL stack name
	void LoadSelectionId(TRenderingID id) const;
	// Set current color
	void SetColor(float* color) const;
	// Operation on matrix
	void LoadMatrix(float* matrix) const;
	void PushMatrix() const;
	void MultMatrix(const float* matrix) const;
	void PopMatrix() const;	
	// Blending parametrization
	void StartBlending() const;
	void EndBlending() const;
	// Shader parametrization
	TRenderingID CreateShader(const std::string& vertexShaderSource, const std::string& fragmentShaderSource);
	void GetShaderLocations(TRenderingID programID, ShaderLocalizator& localizator);
	void EnableShader(TRenderingID programID);
	void DisableShader(TRenderingID programID);
	// Vertex buffer parametrization
	TRenderingID CreateVertexBuffer(VertexBufferType type, int nElement);
	void DeleteVertexBuffer(TRenderingID bufferID);
	void LoadVertexBuffer(TRenderingID bufferID, VertexBufferType type, int nElement, void *data);
	void EnableVertexBuffer(TRenderingID bufferID, VertexBufferType type, int nElement, int location);
	void DisableVertexBuffer(VertexBufferType type, int location);
	void RenderMesh(MeshType meshType, int nElements);
	void RenderIndexedMesh(MeshType meshType, int nElements);
	// Load shader value (uniform value)
	void LoadShaderVariable(int location, const float value) const;
	void LoadShaderVariable(int location, const Vector::Vector3& value) const;	
	void LoadShaderVariable(int location, const Matrix::Matrix4& value) const;
	void LoadShaderVariable(int location, const TITexturePointer& value, int unit) const;	
	// Texture management
	TRenderingID CreateTexture(PixelFormat format, int nLevels, int width, int height, const void* pixels);
	TRenderingID CreateCubeTexture(PixelFormat format, int nLevels, int width, int height, const unsigned char* pixels[6]);
	void LoadTexture(TRenderingID textureID, TextureType type, int unit);	
	void SetTexture(TRenderingID textureID, int width, int height, const unsigned char* data);
	void DeleteTexture(TRenderingID textureID);
	// Targer management
	TRenderingID CreateFrameBuffer(int width, int height);	
	void DeleteFrameBuffer(TRenderingID frameBufferID);
	TRenderingID CreateTarget(TRenderingID frameBufferID, int width, int height, TargetType type, PixelFormat format);
	void EnableFrameBuffer(TRenderingID frameBufferID, int width, int height);
	void DisableFrameBuffer(TRenderingID frameBufferID);
	// Return the renderer instance
	static Renderer& Instance();
private:	
	// Constructor	
	Renderer();
	Renderer(const Renderer& copy);	
	Renderer& operator=(const Renderer& copy);	
};

}
#endif //RENDERER_HPP
